﻿using System;
using System.ComponentModel;
using System.Collections.Generic;
using System.Text;
using System.Windows.Forms;
using System.IO;
using System.Globalization;
using System.Xml;
using System.Threading;

namespace CopyIt
{
    class Copier
    {
        private uint FileToCopy = 0;
        private uint FileCopied = 0;
        private ulong ByteToCopy = 0;
        private ulong ByteCopied = 0;
        private uint Errors = 0;
        private string Filename = "";
        private DateTime timeStart;
        private TreeNodeCollection Nodes;
        private BackgroundWorker bWork = null;
        private CopyEngine CopyEng = null;

        private XmlDocument xmlDoc = null;

        static EventWaitHandle WaitEndCopy = new AutoResetEvent(false);                                

        public event EventHandler BeginSyncronize;
        public event EventHandler EndSyncronize;
        public delegate void CopierHandler(object sender, CopierEventArgs e);
        public event CopierHandler FilesCopied;
        public delegate void UpdateBackupHandler(object sender, UpdateBackupArgs e);
        public event UpdateBackupHandler UpdateBackup;		

        public Copier(TreeNodeCollection Nodes)
        {
            this.Nodes = Nodes;

            CopyEng = new CopyEngine();
            CopyEng.CpEvHandler += new CopyEventHandler(CopyEng_CpEvHandler);
            CopyEng.EndCopy += new EventHandler(CopyEng_EndCopy);

            bWork = new BackgroundWorker();
            bWork.WorkerSupportsCancellation = true;
            bWork.WorkerReportsProgress = true;
            bWork.DoWork += new DoWorkEventHandler(bWork_DoWork);
            bWork.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bWork_RunWorkerCompleted);
            bWork.ProgressChanged += new ProgressChangedEventHandler(bWork_ProgressChanged);
        }

        void CopyEng_EndCopy(object sender, EventArgs e)
        {
            WaitEndCopy.Set();
        }

        void CopyEng_CpEvHandler(CopyEngine sender, CopyEngine.CopyEventArgs e)
        {
            bWork.ReportProgress((int)e.CurrentPercent, e);
        }

        void bWork_ProgressChanged(object sender, ProgressChangedEventArgs e)
        {
            CopyEngine.CopyEventArgs eventarg = (CopyEngine.CopyEventArgs)e.UserState;
            SendInfoToCaller((float)e.ProgressPercentage, eventarg.CurrentTauxTransfert, eventarg.StreamSize);
        }

        void bWork_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            if (EndSyncronize != null)
                EndSyncronize(this, new EventArgs());

            if (CopyEng.StopCopy)
            {
                MessageBox.Show("Operazione interrotta dall'utente", "CopyIT", MessageBoxButtons.OK, MessageBoxIcon.Information);
            }
            else
            {
                TimeSpan diffTime = DateTime.Now - timeStart;
                string testo = string.Format("Operazione Compleatata\nCopiati {0} file in {1}",
                                                FileCopied.ToString("###,###"),
                                                new DateTime(diffTime.Ticks).ToLongTimeString());
                MessageBox.Show(testo, "CopyIT", MessageBoxButtons.OK, MessageBoxIcon.Information);
            }
        }

        void bWork_DoWork(object sender, DoWorkEventArgs e)
        {
            DoStart((string)e.Argument);
        }

        public void StopCopy()
        {
            if (MessageBox.Show("Sei sicuro di voler fermare la copia ?", "Stop dell'utente", MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.Yes)
            {
                CopyEng.StopCopy = true;
                bWork.CancelAsync();
            }
        }

        public bool Start(out string report)
        {
            if (BeginSyncronize != null)
                BeginSyncronize(this, new EventArgs());

            report = string.Concat(@"Logs\CopyIt", DateTime.Now.ToString("-yyyyMMdd-HHmmss"), ".xml");
            bWork.RunWorkerAsync(report);

            return true;
        }

        public bool DoStart(string report)
        {
            XmlNode xmlRoot = null;
            XmlNode xmlFileCopied = null;
            XmlNode xmlErrors = null;

            timeStart = DateTime.Now;
            FileToCopy = 0;
            ByteToCopy = 0;
            CalculateTotals(Nodes, ref FileToCopy, ref ByteToCopy);
            InitializeXMLDocument(out xmlRoot, out xmlFileCopied, out xmlErrors);

            CopyFiles(Nodes, xmlFileCopied, xmlErrors);

            xmlDoc.Save(report);

            return true;
        }

        private void InitializeXMLDocument(out XmlNode xmlRoot, out XmlNode xmlFileCopied, out XmlNode xmlErrors)
        {
            xmlDoc = new XmlDocument();
            xmlDoc.AppendChild(xmlDoc.CreateXmlDeclaration("1.0", "utf-8", "yes"));

            XmlProcessingInstruction newPI;
            String PItext = "type='text/xsl' href='..\\CopyIt.xsl'";
            newPI = xmlDoc.CreateProcessingInstruction("xml-stylesheet", PItext);
            xmlDoc.AppendChild(newPI);

            xmlRoot = xmlDoc.CreateElement("copyit");
            xmlDoc.AppendChild(xmlRoot);

            xmlFileCopied = xmlDoc.CreateElement("filecopied");
            xmlRoot.AppendChild(xmlFileCopied);

            xmlErrors = xmlDoc.CreateElement("errors");
            xmlRoot.AppendChild(xmlErrors);
        }

        private void CopyFiles(TreeNodeCollection nodes, XmlNode xmlFileCopied, XmlNode xmlErrors)
        {
            foreach (ExTreeNode node in nodes)
            {
                if (node.Checked)
                {
                    if (bWork.CancellationPending)
                        return;

                    Filename = node.SourcePath;

                    switch (node.Override)
                    {
                        case OverrideFile.None:
                            if (UpdateBackup != null)
                                UpdateBackup(this, new UpdateBackupArgs(node.Guid));
                            break;
                        case OverrideFile.Create:
                            try
                            {
                                Directory.CreateDirectory(node.SourcePath);
                                CreateNode(xmlFileCopied, "create", "directory", node.SourcePath, "");
                            }
                            catch (Exception exc)
                            {
                                CreateNode(xmlErrors, "create", "directory", node.SourcePath, exc.Message);
                                Errors++;
                            }
                            break;
                        case OverrideFile.Replace:
                            try
                            {
                                ManageOldFile(node.DestPath, node.Write);
                                CopyEng.CopyFiles(node.SourcePath, node.DestPath);
                                WaitEndCopy.WaitOne(1000);
                                CreateNode(xmlFileCopied, "replace", "file", node.DestPath, "");
                                FileCopied++;
                                ByteCopied += node.Size;
                            }
                            catch (Exception exc)
                            {
                                CreateNode(xmlErrors, "replace", "file", node.SourcePath, exc.Message);
                                Errors++;
                            }
                            break;
                        case OverrideFile.Add:
                            try
                            {
                                File.Copy(node.SourcePath, node.DestPath);
                                CreateNode(xmlFileCopied, "add", "file", node.DestPath, "");
                                FileCopied++;
                                ByteCopied += node.Size;
                            }
                            catch (Exception exc)
                            {
                                CreateNode(xmlErrors, "add", "file", node.SourcePath, exc.Message);
                                Errors++;
                            }
                            break;
                    }

                    if (node.Nodes.Count > 0)
                        CopyFiles(node.Nodes, xmlFileCopied, xmlErrors);

                    SendInfoToCaller(0f, 0d, 0);
                }
            }
        }

        private bool ManageOldFile(string file, WriteMode write)
        {
            switch (write)
            {
                case WriteMode.Override:
                    return true;
                case WriteMode.Trash:
                    Microsoft.VisualBasic.FileIO.FileSystem.DeleteFile(file, Microsoft.VisualBasic.FileIO.UIOption.OnlyErrorDialogs, Microsoft.VisualBasic.FileIO.RecycleOption.SendToRecycleBin);
                    return true;
                case WriteMode.Rename:
                    RenameFile(file);
                    return true;
            }
            return true;
        }

        private void RenameFile(string file)
        {
            string dir = Path.GetDirectoryName(file);
            string fil = Path.GetFileNameWithoutExtension(file);
            string ext = Path.GetExtension(file);

            uint count = 0;
            string newfile = "";
            do
            {
                count++;
                newfile = ext != "" 
                    ? string.Format("{0}({1:####}).{2}", fil, count, ext)
                    : string.Format("{0}({1:####})", fil, count);

                newfile = Path.Combine(dir, newfile);
            }
            while (File.Exists(newfile));

            File.Move(file, newfile);
        }

        private void CreateNode(XmlNode father, string operation, string type, string file, string mess)
        {
            XmlNode xNode = null;
            XmlAttribute xaNode = null;

            xNode = father.AppendChild(xmlDoc.CreateElement("execute"));
            xaNode = xmlDoc.CreateAttribute("operation");
            xaNode.Value = operation;
            xNode.Attributes.Append(xaNode);

            xaNode = xmlDoc.CreateAttribute("type");
            xaNode.Value = type;
            xNode.Attributes.Append(xaNode);

            xaNode = xmlDoc.CreateAttribute("name");
            xaNode.Value = file;
            xNode.Attributes.Append(xaNode);

            if (mess != "")
                xNode.InnerText = mess;
        }

        private void SendInfoToCaller(float PercCurFile, double CurrTrasfRate, ulong StremSize)
        {
            if (FilesCopied != null)
            {
                float PercFiles = ((float)FileCopied * 100.0f / (float)FileToCopy);

                CopierEventArgs eventArgs = new CopierEventArgs(Filename, FileToCopy, ByteToCopy, FileCopied, ByteCopied + StremSize, Errors,
                                                                    PercFiles, PercCurFile, CurrTrasfRate);

                FilesCopied(this, eventArgs);
            }
        }

        private void CalculateTotals(TreeNodeCollection nodes, ref uint totFile, ref ulong totByte)
        {
            foreach (ExTreeNode node in nodes)
            {
                if (node.Checked)
                {
                    if (node.Override == OverrideFile.Add || node.Override == OverrideFile.Replace)
                    {
                        totByte += node.Size;
                        totFile++;
                    }
                    if (node.Nodes.Count > 0)
                        CalculateTotals(node.Nodes, ref totFile, ref totByte);
                }
            }

        }
    }

    public class CopierEventArgs : EventArgs
    {
        public string Filename { get; private set; }
        public uint FileToCopy { get; private set; }
        public ulong ByteToCopy { get; private set; }
        public uint FileCopied { get; private set; }
        public ulong ByteCopied { get; private set; }
        public uint Errors { get; private set; }
        public float PercFiles    { get; private set; }
        public float PercCurFile  { get; private set; }
        public double CurTrasfRate { get; private set; }

        public CopierEventArgs(string Filename, uint FileToCopy, ulong ByteToCopy, uint FileCopied,
                                ulong ByteCopied, uint Errors, float PercFiles, float PercCurFile,
                                double CurTrasfRate)
        {
            this.Filename       = Filename;
            this.FileToCopy     = FileToCopy;
            this.ByteToCopy     = ByteToCopy;
            this.FileCopied     = FileCopied;
            this.ByteCopied     = ByteCopied;
            this.Errors         = Errors;

            this.PercCurFile    = PercCurFile;
            this.PercFiles      = PercFiles;
            this.CurTrasfRate   = CurTrasfRate;
        }
    }

        public class UpdateBackupArgs : EventArgs
        {
            public string Guid {get; private set;}

            public UpdateBackupArgs(string guid)
            {
                Guid = guid;
            }
        }
}


